package bancosys.tec.services.implementor.descriptor;

import jmine.tec.di.cache.TypeMatchLookup;
import jmine.tec.di.injector.DependencyInjector;
import jmine.tec.di.resolver.ObjectResolver;
import jmine.tec.di.type.TypeConverter;
import jmine.tec.utils.reflection.fast.FastMethodFactory;
import bancosys.tec.services.implementor.ServiceImplementorAccessor;
import bancosys.tec.services.implementor.UnmodifiableServiceImplementorAccessor;
import bancosys.tec.services.implementor.annotations.visitor.DependencyMetadataParser;

/**
 * Implementacao trivial de {@link ServiceDescriptor}
 * 
 * @author MarceloT
 */
public final class DefaultDescriber implements ServiceDescriptor {

    private final ValidatorDescriptor validatorDescriptor = new ValidatorDescriptor();

    private final ServiceOutputDescriber serviceOutputDescriber = new ServiceOutputDescriber();

    private final InstanceCreatorDescriber creatorDescriber;

    private final DependencyDescriber depDescriber;

    private final ExecutionProxyDescriber execDescriber;

    /**
     * C'tor
     * 
     * @param staticResolvers os resolvedores de objetos estaticos
     * @param typeConvertion o sistema de conversao de tipos
     * @param methodFactory o {@link FastMethodFactory}
     * @param defaultResolver {@link ObjectResolver}
     */
    public DefaultDescriber(TypeMatchLookup<ObjectResolver> staticResolvers, TypeMatchLookup<TypeConverter<?>> typeConvertion,
            FastMethodFactory methodFactory, ObjectResolver defaultResolver) {
        DependencyMetadataParser dependencyMetadataParser = new DependencyMetadataParser(staticResolvers, typeConvertion, defaultResolver);
        creatorDescriber = new InstanceCreatorDescriber(dependencyMetadataParser);
        depDescriber = new DependencyDescriber(dependencyMetadataParser, methodFactory);
        execDescriber = new ExecutionProxyDescriber(dependencyMetadataParser, methodFactory);
    }

    /**
     * @param type type
     * @return {@link ServiceImplementorAccessor}
     */
    public ServiceImplementorAccessor describeService(Class<?> type) {
        if (AbstractServiceAnnotationParser.findTypeWithAnnotation(type) == null) {
            throw new IllegalArgumentException(type + " não tem anotacao @ServiceImplementor!");
        }
        return new UnmodifiableServiceImplementorAccessor(type, this.createDependencyInjector(type), this.createExecutionProxy(type),
                this.createOutputExtractor(type), this.createServiceValidator(type), this.createCreator(type));
    }

    /**
     * @param type cria um 'criador'
     * @return {@link InstanceCreator}
     */
    private InstanceCreator createCreator(Class<?> type) {
        return this.creatorDescriber.findCreator(type);
    }

    /**
     * @param type type
     * @return {@link ServiceValidator}
     */
    private ServiceValidator createServiceValidator(Class<?> type) {
        return this.validatorDescriptor.createValidatorFor(type);
    }

    /**
     * @param type type
     * @return {@link ServiceExecutionProxy}
     */
    private ServiceExecutionProxy createExecutionProxy(Class<?> type) {
        return this.execDescriber.createExecutionProxyFor(type);
    }

    /**
     * @param type type
     * @return {@link DependencyInjector}
     */
    private DependencyInjector createDependencyInjector(Class<?> type) {
        return this.depDescriber.createDependencyInjectorFor(type);
    }

    /**
     * @param type type
     * @return {@link ServiceOutputExtractor}
     */
    private ServiceOutputExtractor createOutputExtractor(Class<?> type) {
        return this.serviceOutputDescriber.createExtractorFor(type);
    }

    /**
     * @return the validatorDescriptor
     */
    public ValidatorDescriptor getValidatorDescriptor() {
        return validatorDescriptor;
    }

    /**
     * @return the serviceOutputDescriber
     */
    public ServiceOutputDescriber getServiceOutputDescriber() {
        return serviceOutputDescriber;
    }

    /**
     * @return the creatorDescriber
     */
    public InstanceCreatorDescriber getCreatorDescriber() {
        return creatorDescriber;
    }

    /**
     * @return the depDescriber
     */
    public DependencyDescriber getDependencyDescriber() {
        return depDescriber;
    }

    /**
     * @return the execDescriber
     */
    public ExecutionProxyDescriber getExecutionDescriber() {
        return execDescriber;
    }

}
